﻿/*
    asyncCommand - used instead of click, while using deferred results,
                   supports enable, allowed and inProgress
*/
ko.asyncCommand = function (exec, enabled, allowed) {
    var command = function () {
        if (command.isEnabled() == false) {
            return;
        }
        command.isInProgress(true);
        var result = exec.apply(this, arguments);
        result.always(function () { command.isInProgress(false); });
        return result;
    };

    command.isInProgress = ko.observable(false);
    command.isAllowed = ko.computed(function () {
        return allowed == undefined || allowed();
    });
    command.isEnabled = ko.computed(function () { return (enabled == undefined || enabled()) && command.isAllowed() && command.isInProgress() == false; });

    return command;
};



/*
Can be called with asyncCommand 

Can be called with confirm function, that is used to confirm command execution
Confirmation could be standard confirm that return true/false or function that returns deferred object
*/
ko.bindingHandlers.command = {
    init: function (element, valueAccessor, allBindingsAccessor, viewModel) {

        var valueAccessorResult = valueAccessor();

        if (typeof (valueAccessorResult) != "function") throw new Error("Command value have to be function");

        var commandConfirm = allBindingsAccessor()["commandConfirm"];

        var executeAccessor;


        // command created with action + confirmation
        if (commandConfirm) {
            if (typeof (commandConfirm) != "function") throw new Error("CommandConfirm value have to be function");

            executeAccessor = function () {
                return function () {
                    var result = commandConfirm(viewModel);

                    // built-in CONFIRM handling
                    if (typeof (result) == "boolean") {
                        if (result) {
                            valueAccessorResult.apply(this, arguments);
                        }
                    }

                    // deffered confirm handling (like jquery-ui Dialog)
                    else {
                        if (!result.then) throw new Error("CommandConfirm result is not deferred object");

                        result.then(function (deferredResult) {
                            if (deferredResult) {
                                valueAccessorResult.apply(this, arguments);
                            }
                        });
                    }

                };
            };
        }
        // command created with action only
        else {
            executeAccessor = function () {
                return function () {
                    valueAccessor().apply(this, arguments);
                };
            };
        }

        ko.bindingHandlers.click.init(element, executeAccessor, allBindingsAccessor, viewModel);

        var enabledAccessor = function () {
            return { disabled: ko.computed(function () { return !valueAccessorResult.isEnabled(); }) };
        };


        // ***
        // *** disabled attribute for button and anchor
        // ***
        if (element.tagName == "BUTTON" || element.tagName == "INPUT") {
            ko.bindingHandlers.attr.update(element, enabledAccessor, allBindingsAccessor, viewModel);
            var attrSubscription = valueAccessorResult.isEnabled.subscribe(function () {
                ko.bindingHandlers.attr.update(element, enabledAccessor, allBindingsAccessor, viewModel);
            });

            // dispose action
            ko.utils.domNodeDisposal.addDisposeCallback(element, function () {
                attrSubscription.dispose();
            });
        }


        // ***
        // *** css binding for other elements
        // ***
        ko.bindingHandlers.css.update(element, enabledAccessor, allBindingsAccessor, viewModel);
        var cssSubscription = valueAccessorResult.isEnabled.subscribe(function () {
            ko.bindingHandlers.css.update(element, enabledAccessor, allBindingsAccessor, viewModel);
        });
        // dispose action
        ko.utils.domNodeDisposal.addDisposeCallback(element, function () {
            cssSubscription.dispose();
        });
    },

    // unsubscribe
    destroy: function (element, valueAccessor, allBindingsAccessor, viewModel) {
        var commandAccessor;
        if (typeof (valueAccessor()) == "function") {
            commandAccessor = valueAccessor();
        }
        else {
            commandAccessor = valueAccessor().action;
        }
        commandAccessor.isEnabled.unsubscribe();
    }
};

/*    apply jQuery plugin on element    */
ko.bindingHandlers.jQuery = {
    init: function (element, valueAccessor, allBindingsAccessor, viewModel) {
        var plugin = valueAccessor();
        $(element)[plugin]();
    }
};

/*    hook for onchange event, that will be called, when observable value changed  */
ko.bindingHandlers.valueExt = {
    init: function (element, valueAccessor, allBindingsAccessor, viewModel) {
        ko.bindingHandlers.value.init(element, valueAccessor, allBindingsAccessor, viewModel);
    },

    update: function (element, valueAccessor, allBindingsAccessor, viewModel) {
        ko.bindingHandlers.value.update(element, valueAccessor, allBindingsAccessor, viewModel);
        $(element).change();
    }
};